package qc.module.qms.algorithm.NumericCalc.dto;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qc.common.core.enums.qms.QmsParamValueDataTypeEnum;
import qc.common.core.utils.LocalDateTimeUtil;
import qc.module.qms.algorithm.NumericCalc.util.QmsParamValueUtil;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * QMS数值计算中的一个参数项的值；根据计算输入参数得到的参数值信息，包含是否有分时段使用的参数值及参数值集合
 * 如果没有分时段使用的参数值可以直接使用唯一的参数值（参数值来源于设置的参数值或参数配置中的默认值）
 *
 * @author QuCheng Tech
 * @since 2025/9/14
 */
public class QmsNumericCalcParamValue {
    private static final Logger logger = LoggerFactory.getLogger(QmsNumericCalcParamValue.class);

    /**
     * 构造方法中传入的参数项，参数项包含参数项基本配置和参数值集合
     */
    public QmsNumericCalcInputParamItem paramItem;
    /**
     * 参数项编码方便后继使用
     */
    public String paramCode;
    /**
     * 是否有任意一个参数值，是否存在有任意一个来源于参数值集合或参数项默认值的参数值
     */
    public boolean hasAny;
    /**
     * 是否有分时段参数值，默认为false
     */
    public boolean hasTimeDivision;
    /**
     * 参数值类型;布尔、整型、小数、字符串
     */
    public QmsParamValueDataTypeEnum valueDataType;
    /**
     * 仅有唯一参数值时的值；如果有分时段参数值时为null
     */
    public Object uniqueParamValue;
    /**
     * 参数项中配置的默认参数值，如果为null表示未设置或无效
     */
    public Object defaultParamValue;
    /**
     * 分时段参数值集合，按更新时间降序排列
     */
    public List<QmsNumericCalcTimeDivisionParamValue> timeDivisionParamValues;

    /**
     * 数值计算一个参数项构造函数，传入计算方法中的一个参数项（包含参数项配置信息和参数值集合）
     *
     * @param paramItem 参数项，包含参数项配置信息和参数值集合
     * @return
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public QmsNumericCalcParamValue(QmsNumericCalcInputParamItem paramItem) {
        this.paramItem = paramItem;
        this.hasAny = false;
        this.hasTimeDivision = false;
        this.uniqueParamValue = null;
        this.defaultParamValue = null;
        //自动根据输入参数项进行解析赋值
        if (this.paramItem != null && this.paramItem.getParam() != null) {
            QmsNumericCalcUseMethodParam param = this.paramItem.getParam();
            this.paramCode = param.getParamcode();
            this.valueDataType = param.getValuedatatype();
            //如果有分时段参数值集合，获取分时段参数值设置
            List<QmsNumericCalcInputParamValue> values = this.paramItem.getValues();
            if (values != null && values.size() > 0x0) {
                this.timeDivisionParamValues = new ArrayList<>();
                //先进行参数值转换和有效性判断
                for (QmsNumericCalcInputParamValue pv : values) {
                    //参数值设置为空或转换无效均不当做有效参数
                    if (StringUtils.isNotBlank(pv.getParamvalue())) {
                        Object valueObj = QmsParamValueUtil.getValidParamValue(this.valueDataType, pv.getParamvalue());
                        if (valueObj != null) {
                            QmsNumericCalcTimeDivisionParamValue timeDivisionParamValue = new QmsNumericCalcTimeDivisionParamValue();
                            timeDivisionParamValue.setParamvalue(valueObj);
                            timeDivisionParamValue.setBegintm(pv.getBegintm());
                            timeDivisionParamValue.setEndtm(pv.getEndtm());
                            timeDivisionParamValue.setRepeatbegintm(pv.getRepeatbegintm());
                            timeDivisionParamValue.setRepeatendtm(pv.getRepeatendtm());
                            timeDivisionParamValue.setUptm(pv.getUptm());

                            this.timeDivisionParamValues.add(timeDivisionParamValue);
                        }
                    }
                }
            }
            //先根据有效的分时段参数值集合获取是否能得到唯一的参数值
            if (this.timeDivisionParamValues != null && this.timeDivisionParamValues.size() > 0x0) {
                //分时段参数值按更新时间降序排列
                this.timeDivisionParamValues = this.timeDivisionParamValues.stream().sorted(Comparator.comparing(QmsNumericCalcTimeDivisionParamValue::getUptm).reversed()).collect(Collectors.toList());
                this.hasAny = true;
                //如果只有一个分时段参数值，并且参数值中未设置任何有效时间段，该参数值作为唯一的参数值
                if (this.timeDivisionParamValues.size() == 0x1) {
                    //如果参数值集合数量为1，表示只有1组参数值，需要判断是否为唯一
                    //如果参数值设置有启用时间段，不能将唯一的1组参数值作为唯一参数值
                    QmsNumericCalcTimeDivisionParamValue defaultParamValue = this.timeDivisionParamValues.get(0x0);
                    if (defaultParamValue.getBegintm() == null && defaultParamValue.getEndtm() == null
                            && defaultParamValue.getRepeatbegintm() == null && defaultParamValue.getRepeatendtm() == null) {
                        //参数值有效时间段全部为null，不分区时间段均有效；此参数值作为唯一参数值
                        this.hasTimeDivision = false;//设置分时段参数标记为false
                        this.uniqueParamValue = defaultParamValue.getParamvalue();
                    } else {
                        //参数值中的任意一个时间不为null，表示有分时段参数值
                        this.hasTimeDivision = true;
                    }
                } else {
                    //参数值集合数量＞1，存在有设置多组参数值；设置有多个分时段的参数值，不管是否有设置有效时间段均当做有多个参数值（不存在唯一参数值）
                    this.hasTimeDivision = true;
                }
            }
            //如果默认参数值不为空，进行数据类型转换和判断
            if (StringUtils.isNotBlank(param.getDefaultvalue())) {
                Object valueObj = QmsParamValueUtil.getValidParamValue(this.valueDataType, param.getDefaultvalue());
                if (valueObj != null) {
                    this.defaultParamValue = valueObj;
                    //判断是否有根据分时段参数值进行赋值；如果没有任何一个参数值并且没有分时段参数值，设置默认参数值为唯一参数值
                    if (this.hasAny == false && this.hasTimeDivision == false) {
                        this.uniqueParamValue = valueObj;
                        this.hasAny = true;//设置有任意一个参数值标记为true，使用默认参数值作为唯一参数值
                    }
                }
            }
        }
    }

    /**
     * 获取指定时间点使用的参数值，Object类型，返回null表示没有匹配的参数值
     *
     * @param time 时间点
     * @return java.lang.Object
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public Object getObjectValue(LocalDateTime time) {
        logger.debug("getObjectValue 获取参数值，参数编码[" + this.paramCode + "]，时间[" + LocalDateTimeUtil.formatDateTime(time) + "]");
        //如果没有任意一个参数值不需要进行任何判断处理，直接返回null
        if (this.hasAny == false) {
            logger.debug("getObjectValue 获取参数值结果为null，参数中没有任何参数值也没有默认值");
            return null;
        } else {
            //如果没有分时段参数值，返回唯一参数值
            if (this.hasTimeDivision == false) {
                logger.debug("getObjectValue 获取参数值结果[" + this.uniqueParamValue + "]，参数中没有分时段参数值使用唯一参数值");
                return this.uniqueParamValue;
            }
            //需要根据时间点获取参数，传入的时间点不能为null
            if (time != null) {
                //有分时段参数值，从分时段参数值遍历获取，满足条件的第一个返回
                if (this.hasTimeDivision == true && this.timeDivisionParamValues != null && this.timeDivisionParamValues.size() > 0x0) {
                    for (QmsNumericCalcTimeDivisionParamValue valueItem : this.timeDivisionParamValues) {
                        //如果没有任何生效时间段设置，返回当前参数
                        if (valueItem.getBegintm() == null && valueItem.getEndtm() == null
                                && valueItem.getRepeatbegintm() == null && valueItem.getRepeatendtm() == null) {
                            logger.debug("getObjectValue 获取参数值结果[" + valueItem.getParamvalue() + "]，参数值更新时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getUptm()) + "]，该参数值未设置任何使用时段限制");
                            return valueItem.getParamvalue();
                        }
                        //如果生效起始时间在传入的时间点之后，当前参数值不返回
                        if (valueItem.getBegintm() != null && valueItem.getBegintm().isAfter(time)) {
                            logger.debug("getObjectValue 不使用该参数值[" + valueItem.getParamvalue() + "]，参数值更新时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getUptm())
                                    + "]，该参数值有效起始时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getBegintm()) + "]＞数据时间点[" + LocalDateTimeUtil.formatDateTime(time) + "]");
                            continue;
                        }

                        //如果生效截止时间在传入的时间点之前，当前参数值不返回
                        if (valueItem.getEndtm() != null && valueItem.getEndtm().isBefore(time)) {
                            logger.debug("getObjectValue 不使用该参数值[" + valueItem.getParamvalue() + "]，参数值更新时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getUptm())
                                    + "]，该参数值有效截止时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getEndtm()) + "]＜数据时间点[" + LocalDateTimeUtil.formatDateTime(time) + "]");
                            continue;
                        }

                        //如果每年重复的生效起始时间在传入的时间点之后，当前参数不返回;替换重复时间中的年份
                        if (valueItem.getRepeatbegintm() != null && valueItem.getRepeatbegintm().withYear(time.getYear()).isAfter(time)) {
                            logger.debug("getObjectValue 不使用该参数值[" + valueItem.getParamvalue() + "]，参数值更新时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getUptm())
                                    + "]，该参数值每年重复有效起始时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getRepeatbegintm()) + "]＞数据时间点[" + LocalDateTimeUtil.formatDateTime(time) + "]");
                            continue;
                        }
                        //如果每年重复的生效截止时间在传入的时间点之前，当前参数值不返回;替换重复时间中的年份
                        if (valueItem.getRepeatendtm() != null && valueItem.getRepeatendtm().withYear(time.getYear()).isBefore(time)) {
                            logger.debug("getObjectValue 不使用该参数值[" + valueItem.getParamvalue() + "]，参数值更新时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getUptm())
                                    + "]，该参数值每年重复有效截止时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getRepeatendtm()) + "]＜数据时间点[" + LocalDateTimeUtil.formatDateTime(time) + "]");
                            continue;
                        }

                        //判断起止时间结束，当前参数值有效，返回当前参数值
                        logger.debug("getObjectValue 获取参数值结果[" + valueItem.getParamvalue() + "]，参数值更新时间[" + LocalDateTimeUtil.formatDateTime(valueItem.getUptm()) + "]");
                        return valueItem.getParamvalue();
                    }
                }
            }
            //其他及默认情况，返回默认参数值；默认参数值为null时即返回null
            logger.debug("getObjectValue 获取参数值结果[" + this.defaultParamValue + "]，分时段参数值没有适用当前时间点的参数值使用参数项默认值");
            return this.defaultParamValue;
        }
    }

    /**
     * 获取指定时间点使用的boolean类型参数值，无匹配参数时使用指定的默认值
     *
     * @param time         时间点
     * @param defaultValue 默认值
     * @return java.lang.boolean
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public boolean getBoolValue(LocalDateTime time, boolean defaultValue) {
        Boolean value = this.getBooleanValue(time);
        if (value != null)
            return value.booleanValue();

        return defaultValue;
    }

    /**
     * 获取指定时间点使用的Boolean类型参数值，返回null表示没有匹配的参数值
     *
     * @param time 时间点
     * @return java.lang.Boolean
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public Boolean getBooleanValue(LocalDateTime time) {
        Object objValue = this.getObjectValue(time);
        if (objValue != null)
            return (Boolean) objValue;
        return null;
    }

    /**
     * 获取指定时间点使用的int类型参数值，无匹配参数时使用指定的默认值
     *
     * @param time         时间点
     * @param defaultValue 默认值
     * @return java.lang.boolean
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public int getIntValue(LocalDateTime time, int defaultValue) {
        Integer value = this.getIntegerValue(time);
        if (value != null)
            return value.intValue();

        return defaultValue;
    }

    /**
     * 获取指定时间点使用的Integer类型参数值，返回null表示没有匹配的参数值
     *
     * @param time 时间点
     * @return java.lang.Integer
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public Integer getIntegerValue(LocalDateTime time) {
        Object objValue = this.getObjectValue(time);
        if (objValue != null)
            return (Integer) objValue;
        return null;
    }

    /**
     * 获取指定时间点使用的double类型参数值，无匹配参数时使用指定的默认值
     *
     * @param time         时间点
     * @param defaultValue 默认值
     * @return java.lang.boolean
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public double getDoubleValue(LocalDateTime time, double defaultValue) {
        Double value = this.getDoubleValue(time);
        if (value != null)
            return value.doubleValue();

        return defaultValue;
    }

    /**
     * 获取指定时间点使用的Double类型参数值，返回null表示没有匹配的参数值
     *
     * @param time 时间点
     * @return java.lang.Double
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public Double getDoubleValue(LocalDateTime time) {
        Object objValue = this.getObjectValue(time);
        if (objValue != null)
            return (Double) objValue;
        return null;
    }

    /**
     * 获取指定时间点使用的Date类型参数值，无匹配参数时使用指定的默认值
     *
     * @param time         时间点
     * @param defaultValue 默认值
     * @return java.lang.boolean
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public Date getDateValue(LocalDateTime time, Date defaultValue) {
        Date value = this.getDateValue(time);
        if (value != null)
            return value;

        return defaultValue;
    }

    /**
     * 获取指定时间点使用的Date类型参数值，返回null表示没有匹配的参数值
     *
     * @param time 时间点
     * @return java.lang.Date
     * @author QuCheng Tech
     * @since 2025/9/15
     */
    public Date getDateValue(LocalDateTime time) {
        Object objValue = this.getObjectValue(time);
        if (objValue != null)
            return (Date) objValue;
        return null;
    }

    /**
     * 调试使用，输出当前参数值信息
     *
     * @return void
     * @author QuCheng Tech
     * @since 2025/9/17
     */
    public void debugOutput() {
        logger.debug("数值计算参数值--输出开始");
        if (this.paramItem != null && this.paramItem.getParam() != null) {
            QmsNumericCalcUseMethodParam param = this.paramItem.getParam();
            logger.debug("参数编码：" + param.getParamcode());
            logger.debug("是否有任意一个参数值：" + this.hasAny);
            logger.debug("是否有分时段参数值：" + this.hasTimeDivision);
            logger.debug("参数值类型：" + this.valueDataType.getName());
            logger.debug("唯一参数值：" + this.uniqueParamValue);
            logger.debug("默认参数值：" + this.defaultParamValue);
            if (this.timeDivisionParamValues != null && this.timeDivisionParamValues.size() > 0x0) {
                logger.debug("参数值\t更新时间\t起始有效时间\t截止有效时间\t每年起始有效时间\t每年截止有效时间");
                for (QmsNumericCalcTimeDivisionParamValue pv : this.timeDivisionParamValues) {
                    logger.debug(pv.getParamvalue() + "\t" + LocalDateTimeUtil.formatDateTime(pv.getUptm()) + "\t"
                            + LocalDateTimeUtil.formatDateTime(pv.getBegintm()) + "\t" + LocalDateTimeUtil.formatDateTime(pv.getEndtm())
                            + "\t" + LocalDateTimeUtil.formatDateTime(pv.getRepeatbegintm()) + "\t" + LocalDateTimeUtil.formatDateTime(pv.getRepeatendtm()));
                }
            }
        }

        logger.debug("数值计算参数值--输出结束");
    }
}
